﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Diagnostics;

namespace Kindohm.KSynth.UI
{
    public partial class NoteGrid : UserControl
    {
        bool sizing;
        bool sizeable;
        Point previous;
        double leftSnap;
        double rightSnap;
        double nextWidth;

        double xLineSpacing = 10;
        double yLineSpacing = 12;
        double labelWidth = 30;

        Rectangle currentRectangle;
        bool clicking;

        int steps;
        int notes;

        Dictionary<Rectangle, GridNoteInfo> noteInfo;

        public NoteGrid()
        {
            this.noteInfo = new Dictionary<Rectangle, GridNoteInfo>();
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(NoteGrid_Loaded);
            this.canvas.MouseLeftButtonDown += new MouseButtonEventHandler(canvas_MouseLeftButtonDown);
            this.canvas.MouseMove += new MouseEventHandler(canvas_MouseMove);
            this.canvas.MouseLeftButtonUp += new MouseButtonEventHandler(canvas_MouseLeftButtonUp);
            this.MinSize = 10;
            this.Labels = new Dictionary<int, TextBlock>();
        }

        public Dictionary<int, TextBlock> Labels { get; protected set; }

        public double ScrollHeight
        {
            get { return this.scroll.Height; }
            set { this.scroll.Height = value; }
        }

        public double ScrollWidth
        {
            get { return this.scroll.Width; }
            set { this.scroll.Width = value; }
        }

        public int Notes
        {
            get { return this.notes; }
            set
            {
                this.notes = value;
                this.OnNotesChanged();
            }
        }

        public int Steps
        {
            get { return this.steps; }
            set
            {
                this.steps = value;
                this.OnStepsChanged();
            }
        }

        void OnStepsChanged()
        {
            if (this.Steps > 0 && this.Notes > 0)
                this.MakeGridLines();
        }

        void OnNotesChanged()
        {
            if (this.Steps > 0 && this.Notes > 0)
                this.MakeGridLines();
        }

        void canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (!sizing & !clicking)
            {
                Point position = e.GetPosition(this.canvas);

                if (position.X - this.labelWidth > 0)
                {
                    int column = (int)(position.X - this.labelWidth) / (int)xLineSpacing;
                    double xPosition = column * xLineSpacing + this.labelWidth;

                    int row = (int)position.Y / (int)yLineSpacing;
                    double yPosition = row * yLineSpacing;

                    Rectangle rect = this.MakeRectangle();
                    rect.SetValue(Canvas.LeftProperty, xPosition);
                    rect.SetValue(Canvas.TopProperty, yPosition);
                    this.canvas.Children.Add(rect);

                    int length = (int)rect.Width / (int)this.xLineSpacing;
                    GridNoteInfo info = new GridNoteInfo() { Row = row, Column = column, Length = length };
                    this.noteInfo.Add(rect, info);
                    this.OnNoteAdded(info.Row, info.Column, info.Length);
                }
            }
        }

        Rectangle MakeRectangle()
        {
            Rectangle rect = new Rectangle();
            rect.Width = this.xLineSpacing;
            rect.Height = yLineSpacing;
            rect.Fill = new SolidColorBrush(Colors.Cyan);
            rect.Stroke = new SolidColorBrush(Colors.White);
            rect.StrokeThickness = 1;

            rect.MouseMove += new MouseEventHandler(rect_MouseMove);
            rect.MouseLeftButtonDown += new MouseButtonEventHandler(rect_MouseLeftButtonDown);
            rect.MouseLeftButtonUp += new MouseButtonEventHandler(rect_MouseLeftButtonUp);
            rect.MouseLeave += new MouseEventHandler(rect_MouseLeave);

            return rect;
        }


        void NoteGrid_Loaded(object sender, RoutedEventArgs e)
        {
            //this.MakeGridLines();
        }

        void MakeGridLines()
        {
            this.labelText = "c";
            this.Labels.Clear();
            this.canvas.Children.Clear();
            this.currentRectangle = null;
            this.clicking = false;
            this.sizeable = false;
            this.sizing = false;
            this.previous.X = double.MinValue;
            int elapsed = 0;
            int xCount = this.Steps + 1;

            for (int x = 0; x < xCount; x++)
            {
                Line line = new Line();
                line.IsHitTestVisible = false;
                line.X1 = x * xLineSpacing + this.labelWidth;
                line.X2 = x * xLineSpacing + this.labelWidth;
                line.Y1 = 0;
                line.Y2 = this.Notes * yLineSpacing - yLineSpacing;
                if (elapsed == 0)
                    line.Stroke = new SolidColorBrush(Colors.LightGray);
                else
                    line.Stroke = new SolidColorBrush(Colors.Gray);
                line.StrokeThickness = 1;
                this.canvas.Children.Add(line);
                elapsed++;
                if (elapsed > 3)
                    elapsed = 0;
            }

            int octCount = 10;

            for (int y = 0; y < this.Notes; y++)
            {
                Line line = new Line();
                line.IsHitTestVisible = false;
                line.X1 = 0;
                line.X2 = xCount * xLineSpacing - xLineSpacing + this.labelWidth;
                line.Y1 = y * yLineSpacing;
                line.Y2 = y * yLineSpacing;
                line.StrokeThickness = 1;

                octCount++;
                if (octCount == 12)
                {
                    octCount = 0;
                    line.Stroke = new SolidColorBrush(Colors.LightGray);
                }
                else
                    line.Stroke = new SolidColorBrush(Colors.Gray);

                this.canvas.Children.Add(line);

                if (y < this.Notes - 1)
                {
                    TextBlock label = new TextBlock();
                    label.Text = this.labelText;
                    this.SetNextLabel();
                    this.canvas.Children.Add(label);
                    label.SetValue(Canvas.LeftProperty, 0d);
                    label.SetValue(Canvas.TopProperty, (double)(y * yLineSpacing - 3));
                    label.Foreground = new SolidColorBrush(Colors.Gray);
                    label.MouseLeftButtonUp += new MouseButtonEventHandler(label_MouseLeftButtonUp);
                    label.Tag = y;
                    this.Labels.Add(y, label);
                }
            }

            this.canvas.Width = (xCount - 1) * xLineSpacing + labelWidth; // +200;
            this.canvas.Height = (this.Notes - 1) * yLineSpacing;
            this.wrapper.Width = this.canvas.Width + 10;
            this.wrapper.Height = this.canvas.Height + 10;

            

        }

        public void SetLabelText(int row, string text)
        {
            if (this.Labels.ContainsKey(row))
                this.Labels[row].Text = text;
        }

        string labelText = "c";
        void SetNextLabel()
        {
            if (this.labelText == "b")
                this.labelText = "a#";
            else if (this.labelText == "a#")
                this.labelText = "a";
            else if (this.labelText == "a")
                this.labelText = "g#";
            else if (this.labelText == "g#")
                this.labelText = "g";
            else if (this.labelText == "g")
                this.labelText = "f#";
            else if (this.labelText == "f#")
                this.labelText = "f";
            else if (this.labelText == "f")
                this.labelText = "e";
            else if (this.labelText == "e")
                this.labelText = "d#";
            else if (this.labelText == "d#")
                this.labelText = "d";
            else if (this.labelText == "d")
                this.labelText = "c#";
            else if (this.labelText == "c#")
                this.labelText = "c";
            else if (this.labelText == "c")
                this.labelText = "b";
        }

        void label_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (this.GridRowLabelClicked != null)
            {
                int row = (int)((TextBlock)sender).Tag;
                GridRowLabelClickedEventArgs args = new GridRowLabelClickedEventArgs() { Row = row };
                this.GridRowLabelClicked(this, args);
            }
        }

        public double MinSize { get; set; }

        void canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (this.currentRectangle != null)
            {
                this.sizing = false;
                this.previous.X = double.MinValue;
                this.nextWidth = this.currentRectangle.Width;
                this.Cursor = Cursors.Arrow;
                this.currentRectangle = null;
            }
        }

        void canvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (sizing)
            {
                Point position = e.GetPosition(this.currentRectangle);
                if (this.previous != null && this.previous.X != double.MinValue)
                {
                    this.nextWidth = this.nextWidth + (position.X - this.previous.X);

                    if (((this.nextWidth < (leftSnap + 1))
                        | (this.nextWidth > (rightSnap - 1))
                        )
                        & this.nextWidth > this.MinSize)
                    {

                        if (Math.Abs(leftSnap - this.nextWidth) < Math.Abs(this.nextWidth - rightSnap))
                        {
                            this.currentRectangle.Width = leftSnap;
                        }
                        else
                        {
                            this.currentRectangle.Width = rightSnap;
                        }

                        GridNoteInfo info = this.noteInfo[this.currentRectangle];
                        info.Length = (int)this.currentRectangle.Width / (int)this.xLineSpacing;

                        this.OnNoteResized(info.Row, info.Column, info.Length);

                        this.leftSnap = this.currentRectangle.Width - xLineSpacing;
                        this.rightSnap = this.currentRectangle.Width + xLineSpacing;

                    }
                }
                this.previous = position;
            }
        }

        void rect_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (this.currentRectangle != null)
            {
                if (!clicking)
                {
                    this.sizing = false;
                    this.previous.X = double.MinValue;
                    this.nextWidth = this.currentRectangle.Width;
                    this.Cursor = Cursors.Arrow;
                }
                else
                {
                    GridNoteInfo info = this.noteInfo[this.currentRectangle];
                    this.canvas.Children.Remove(this.currentRectangle);
                    this.clicking = false;
                    this.OnNoteDeleted(info.Row, info.Column);
                }
                this.currentRectangle = null;
            }
        }

        void rect_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.currentRectangle = (Rectangle)sender;
            if (sizeable)
            {
                this.sizing = true;
                this.leftSnap = this.currentRectangle.Width - xLineSpacing;
                this.rightSnap = this.currentRectangle.Width + xLineSpacing;
                this.previous = new Point(double.MinValue, 0);
                this.nextWidth = this.currentRectangle.Width;
            }
            else
            {
                this.clicking = true;
            }
        }

        void rect_MouseMove(object sender, MouseEventArgs e)
        {
            if (!sizing)
            {
                Rectangle rect = (Rectangle)sender;
                Point position = e.GetPosition(rect);
                if (position.X > rect.Width - 5)
                {
                    this.Cursor = Cursors.SizeWE;
                    this.sizeable = true;
                }
                else
                {
                    this.Cursor = Cursors.Arrow;
                    this.sizeable = false;
                }
            }
        }

        void rect_MouseLeave(object sender, MouseEventArgs e)
        {
            if (!sizing)
            {
                this.Cursor = Cursors.Arrow;
            }
        }

        void OnNoteAdded(int row, int column, int length)
        {
            GridNoteChangedEventArgs args = new GridNoteChangedEventArgs();
            args.Column = column;
            args.Row = row;
            args.New = true;
            args.Length = length;
            this.RaiseGridNoteChangedEvent(args);
        }

        void OnNoteResized(int row, int column, int length)
        {
            GridNoteChangedEventArgs args = new GridNoteChangedEventArgs();
            args.Column = column;
            args.Row = row;
            args.New = false;
            args.Length = length;
            this.RaiseGridNoteChangedEvent(args);
        }

        void OnNoteDeleted(int row, int column)
        {
            GridNoteChangedEventArgs args = new GridNoteChangedEventArgs();
            args.Column = column;
            args.Row = row;
            args.New = false;
            args.Deleted = true;
            this.RaiseGridNoteChangedEvent(args);
        }

        void RaiseGridNoteChangedEvent(GridNoteChangedEventArgs args)
        {
            if (this.GridNoteChanged != null)
                this.GridNoteChanged(this, args);
        }

        public event EventHandler<GridNoteChangedEventArgs> GridNoteChanged;
        public event EventHandler<GridRowLabelClickedEventArgs> GridRowLabelClicked;

    }
}
